<template>
  <div class="big-file-uploader">
    <a-card :bordered="false" class="upload-card">
      <template #title>
        <div class="upload-title">
          <h5>文件上传</h5>
          <a-tooltip placement="rightTop">
            <template #title>
              <span>当上传的文件大于5MB时，自动使用大文件分片上传（显示上传进度条）</span>
            </template>
            <QuestionCircleOutlined />
          </a-tooltip>
        </div>
      </template>
      <template #extra>
        <a-button v-if="selectedFile" @click="clearUpload" type="text"> <a-icon type="close-circle" /> 清除 </a-button>
      </template>

      <div class="upload-area">
        <a-upload :show-upload-list="false" :beforeUpload accept="*">
          <a-button :disabled="uploading" type="primary"> <a-icon type="upload" /> 选择文件 </a-button>
        </a-upload>

        <div v-if="selectedFile" class="file-info">
          <div class="file-header">
            <div class="file-name">{{ selectedFile.name }}</div>
            <div class="file-size">{{ formatFileSize(selectedFile.size) }}</div>
          </div>

          <div v-if="!useSingle" class="progress-container">
            <a-progress :percent="uploadProgress" :status="progressStatus" class="upload-progress" />
            <div class="progress-text">
              {{ progressText }}
            </div>
          </div>

          <div class="upload-actions">
            <a-button v-if="!uploading && !uploadSuccess" type="primary" @click="startUpload" :loading="checkingChunks">
              {{ checkingChunks ? '检查分片...' : '开始上传' }}
            </a-button>
            <!-- <a-button v-if="uploading" type="danger" @click="abortUpload"> 取消上传 </a-button> -->
          </div>

          <div v-if="uploadError" class="error-message"> <a-icon type="exclamation-circle" /> {{ uploadErrorMessage }} </div>

          <div v-if="uploadSuccess" class="success-message"> <a-icon type="check-circle" /> 上传成功 </div>
        </div>
      </div>
    </a-card>
  </div>
</template>

<script setup lang="ts">
  import { reactive, onUnmounted, toRefs, computed } from 'vue';
  import type { Chunk, UploaderProps, UploaderEmits, ChunkError, UploadResult } from '/@/components/Common/largeFileUpload/src/typing';
  import { formatFileSize, calculateFileHash, createChunks } from './helper';
  import { useMessage } from '/@/hooks/web/useMessage';
  import { initPartUpload, uploadPartFile, completeParts, removePartFile, singleUpload } from './api';
  import { QuestionCircleOutlined } from '@ant-design/icons-vue';

  defineOptions({ name: 'LargeFileUpload' });
  const { createMessage } = useMessage();

  const props = withDefaults(defineProps<UploaderProps>(), {
    chunkSize: 5, // 分片大小 单位:MB
    timeOut: 30, // 上传超时时间 默认30s
    concurrentUploads: 3, // 并发上传的分片数量
    autoStart: false, // 选择文件后是否自动开始上传
    retryCount: 3, // 分片上传失败后的重试次数
    allowedFileTypes: () => [], // 允许上传的文件类型
    maxFileSize: Infinity,
    useLargeSize: 5 * 1024 * 1024, // 使用分片上传的阈值，大于设定值将使用分片上传
    showProgress: true,
    showRetry: true,
    successMessage: '上传成功',
    errorMessage: '上传失败',
  });

  const emits = defineEmits<UploaderEmits>();

  const state = reactive({
    selectedFile: null as File | null,
    fileHash: '',
    chunks: [] as Chunk[],
    uploadedChunks: new Set<number>(),
    uploadProgress: 0,
    uploading: false,
    uploadSuccess: false,
    uploadError: false,
    uploadErrorMessage: '',
    checkingChunks: false,
    controllers: [] as AbortController[], // 存储所有分片上传的 AbortController 实例，用于取消正在进行的上传请求。
    chunkRetryCount: {} as Record<number, number>,
    objectKey: '', // 后端返回的文件上传 objectKey
    uploadId: '', // 后端返回的文件上传 uploadId
  });

  const { selectedFile, fileHash, chunks, uploadProgress, uploading, uploadSuccess, uploadError, uploadErrorMessage, checkingChunks } = toRefs(state);

  const progressStatus = computed<string>(() => {
    if (uploading.value && uploadProgress.value < 100) return 'active';
    if (uploadSuccess.value) return 'success';
    if (uploadError.value) return 'exception';
    return 'normal';
  });

  const progressText = computed<string>(() => {
    if (uploading.value) return `上传中 ${uploadProgress.value}%`;
    if (uploadSuccess.value) return props.successMessage;
    if (uploadError.value) return props.errorMessage;
    return '等待上传';
  });

  const useSingle = computed<boolean>(() => {
    // 文件小于5MB时，使用单文件上传
    return selectedFile.value!.size < props.useLargeSize;
  });

  // 文件选择前置处理
  const beforeUpload = (file: File): boolean => {
    clearUpload();
    // 文件类型验证
    if (props.allowedFileTypes.length > 0) {
      const fileType = file.type;
      const isAllowed = props.allowedFileTypes.some((type) => fileType === type || (type.startsWith('.') && file.name.endsWith(type)));

      if (!isAllowed) {
        createMessage.error(`不支持的文件类型: ${fileType}`);
        return false;
      }
    }

    // 文件大小验证
    if (file.size > props.maxFileSize) {
      createMessage.error(`文件大小超过限制: ${formatFileSize(props.maxFileSize)}`);
      return false;
    }

    state.selectedFile = file;
    state.uploadSuccess = false;
    state.uploadError = false;

    if (props.autoStart) {
      startUpload();
    }

    return false; // 阻止默认上传
  };

  // 分片检查
  // const checkUploadedChunks = async (): Promise<void> => {
  //   if (!fileHash.value || !selectedFile.value) return;

  //   state.checkingChunks = true;

  //   try {
  //     const params = {
  //       fileName: selectedFile.value.name,
  //       fileSize: selectedFile.value.size,
  //       fileType: selectedFile.value.type,
  //     };
  //     const response: any = await initPartUpload(params);
  //     console.log(response);

  //     if (!response.ok) {
  //       throw new Error(`HTTP错误: ${response.status}`);
  //     }

  //     const data = await response.json();
  //     if (data.uploadedChunks && Array.isArray(data.uploadedChunks)) {
  //       data.uploadedChunks.forEach((chunkIndex: number) => {
  //         state.uploadedChunks.add(chunkIndex);

  //         const chunk = state.chunks.find((c) => c.index === chunkIndex);
  //         if (chunk) {
  //           chunk.uploaded = true;
  //         }
  //       });

  //       updateProgress();

  //       if (data.uploadedChunks.length === state.chunks.length) {
  //         state.uploadProgress = 100;
  //         state.uploading = false;
  //         state.uploadSuccess = true;

  //         emits('upload-success', {
  //           fileHash: fileHash.value,
  //           fileName: selectedFile.value.name,
  //           fileSize: selectedFile.value.size,
  //           totalChunks: state.chunks.length,
  //         } as UploadResult);
  //       }
  //     }
  //   } catch (error) {
  //     state.uploadError = true;
  //     state.uploadErrorMessage = '检查分片失败';
  //     emits('upload-error', { error: error as Error });
  //   } finally {
  //     state.checkingChunks = false;
  //   }
  // };

  // 上传单个分片
  const uploadChunk = async (chunk: Chunk): Promise<boolean> => {
    if (!fileHash.value || !selectedFile.value || !chunk) return false;

    const controller = new AbortController();
    state.controllers.push(controller);

    const formData: any = new FormData();
    formData.append('partNumber', chunk.index + 1);
    formData.append('uploadId', state.uploadId);
    formData.append('objectKey', state.objectKey);
    formData.append('totalParts', chunk.total);
    formData.append('file', chunk.chunk);
    try {
      await uploadPartFile(formData, props.timeOut);
      console.log(`分片 ${chunk.index + 1} 上传成功`);

      chunk.uploaded = true;
      state.uploadedChunks.add(chunk.index);

      updateProgress();
      emits('chunk-uploaded', {
        chunkIndex: chunk.index,
        totalChunks: chunk.total,
        fileHash: fileHash.value,
      });

      return true;
    } catch (error) {
      if ((error as Error).name === 'AbortError') {
        console.log('上传已取消');
        return false;
      }

      chunk.retryCount++;
      state.chunkRetryCount[chunk.index] = (state.chunkRetryCount[chunk.index] || 0) + 1;

      if (props.showRetry && state.chunkRetryCount[chunk.index] < props.retryCount) {
        console.log(`分片 ${chunk.index} 上传失败，重试 ${state.chunkRetryCount[chunk.index]}/${props.retryCount}`);
        return uploadChunk(chunk);
      } else {
        state.uploadError = true;
        state.uploadErrorMessage = `分片 ${chunk.index} 上传失败，已达到最大重试次数`;
        emits('chunk-error', {
          chunkIndex: chunk.index,
          fileHash: fileHash.value,
          error: error as Error,
        } as ChunkError);
        return false;
      }
    }
  };

  // 更新上传进度
  const updateProgress = (): void => {
    const uploadedCount = state.chunks.filter((chunk) => chunk.uploaded).length;
    state.uploadProgress = Math.round((uploadedCount / state.chunks.length) * 90);
    emits('upload-progress', state.uploadProgress);
  };

  // 并发上传分片
  const uploadChunks = async (): Promise<void> => {
    if (!selectedFile.value || !chunks.value || chunks.value.length === 0) return;

    state.uploading = true;
    state.uploadError = false;
    state.uploadErrorMessage = '';

    const pendingChunks = chunks.value.filter((chunk) => !chunk.uploaded);
    // 所有上传完成则执行合并逻辑
    if (pendingChunks.length === 0) {
      completeUpload();
      return;
    }

    emits('upload-start', {
      fileHash: fileHash.value,
      fileName: selectedFile.value.name,
      fileSize: selectedFile.value.size,
      totalChunks: chunks.value.length,
    });

    let concurrentCount = 0;
    let currentIndex = 0;

    const uploadNextChunk = async (): Promise<void> => {
      if (currentIndex >= pendingChunks.length || !uploading.value) return;

      const chunk = pendingChunks[currentIndex];
      currentIndex++;
      concurrentCount++;

      const success = await uploadChunk(chunk);

      concurrentCount--;

      if (!success && !uploadError.value) {
        state.uploading = false;
        emits('upload-error', {
          error: new Error('分片上传失败'),
          chunkIndex: chunk.index,
        });
        await clearUploadParts();
        return;
      }

      const allUploaded = chunks.value.every((chunk) => chunk.uploaded);
      if (allUploaded) {
        completeUpload();
        return;
      }

      if (concurrentCount < props.concurrentUploads) {
        uploadNextChunk();
      }
    };

    const initialUploads = Math.min(props.concurrentUploads, pendingChunks.length);
    for (let i = 0; i < initialUploads; i++) {
      uploadNextChunk();
    }
  };

  // 完成上传
  const completeUpload = async (): Promise<void> => {
    if (!fileHash.value || !selectedFile.value) return;

    try {
      const params = {
        objectKey: state.objectKey,
        uploadId: state.uploadId,
      };
      const data = await completeParts(params);

      state.uploadSuccess = true;
      state.uploadProgress = 100;
      state.uploading = false;
      emits('upload-success', {
        fileHash: fileHash.value,
        fileName: selectedFile.value.name,
        fileSize: selectedFile.value.size,
        fileUrl: data.url || '',
        bucketName: data.bucketName,
        fileKey: data.fileKey,
        appId: data.appId,
      } as UploadResult);

      createMessage.success(props.successMessage);
    } catch (error) {
      state.uploadError = true;
      state.uploadErrorMessage = '分片上传失败';
      emits('upload-error', { error: error as Error });
      await clearUploadParts();
    }
  };

  // 单文件上传
  const handleSingleUpload = async () => {
    if (!selectedFile.value) {
      createMessage.warning('请先选择文件');
      return;
    }

    const res = await singleUpload(
      {
        file: selectedFile.value,
        fileName: selectedFile.value.name,
        busMsg: '',
        dir: '',
      },
      props.timeOut
    );
    if (res && res.fileKey) {
      createMessage.success('文件上传成功！');
      emits('upload-success', res as UploadResult);
    }
  };

  // 开始上传
  const startUpload = async (): Promise<void> => {
    if (!selectedFile.value) {
      createMessage.warning('请先选择文件');
      return;
    }

    if (useSingle.value) {
      handleSingleUpload();
      // 执行单文件上传逻辑
      return;
    }

    if (uploading.value) return;

    if (!fileHash.value || chunks.value.length === 0) {
      try {
        createMessage.info('正在准备上传...');

        state.fileHash = await calculateFileHash(selectedFile.value);
        state.chunks = createChunks(selectedFile.value, props.chunkSize);
        const params = {
          fileName: selectedFile.value.name,
          fileSize: selectedFile.value.size,
          fileType: selectedFile.value.type,
        };
        const { objectKey, uploadId }: any = await initPartUpload(params);
        state.objectKey = objectKey;
        state.uploadId = uploadId;

        if (!uploadSuccess.value) {
          uploadChunks();
        }
      } catch (error) {
        clearUpload();
        createMessage.warning('上传失败，请稍后重试');
        emits('upload-error', { error: error as Error });
      }
    } else {
      uploadChunks();
    }
  };

  // 清除分片
  const clearUploadParts = async (): Promise<void> => {
    await removePartFile({ objectKey: state.objectKey, uploadId: state.uploadId });
  };

  // 取消上传
  const abortUpload = (): void => {
    state.controllers.forEach((controller) => controller.abort());
    state.controllers = [];
    state.uploading = false;
    createMessage.info('上传已取消');
  };

  // 清除上传
  const clearUpload = (): void => {
    emits('on-clear');
    uploading.value && abortUpload();
    state.selectedFile = null;
    state.fileHash = '';
    state.chunks = [];
    state.uploadedChunks.clear();
    state.uploadProgress = 0;
    state.uploading = false;
    state.uploadSuccess = false;
    state.uploadError = false;
    state.uploadErrorMessage = '';
    state.controllers = [];
    state.chunkRetryCount = {};
    state.objectKey = '';
    state.uploadId = '';
  };

  // 组件卸载时清除定时器
  onUnmounted(() => uploading.value && abortUpload());
</script>

<style scoped lang="less">
  @import url('./style.less');
</style>
